---
image: /generated/articles-docs-webcodecs-track-transformation.png
id: track-transformation
title: Track Transformation
slug: /webcodecs/track-transformation
crumb: '@remotion/webcodecs'
---

:::warning
[We are phasing out Remotion WebCodecs and are moving to Mediabunny](/blog/mediabunny)!
:::

When transforming media, there are multiple thing that can be done for each audio or video track:

- Copying the track without re-encoding
- Re-encoding the track into a different codec
- Removing the track

[`@remotion/webcodecs`](/docs/webcodecs) allows you to decide for each track what to do with it.

## Using the defaults

The minimum amount of configuration is to only specify a [`src`](/docs/webcodecs/convert-media#src) and an output [`container`](/docs/webcodecs/convert-media#container).

```tsx twoslash title="Using the default codecs"
import {convertMedia} from '@remotion/webcodecs';

await convertMedia({
  src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
  container: 'webm',
});
```

The default codecs are defined by [`getDefaultAudioCodec()`](/docs/webcodecs/get-default-audio-codec) and [`getDefaultVideoCodec()`](/docs/webcodecs/get-default-video-codec).

## Choosing codecs

You can use the [`videoCodec`](/docs/webcodecs/convert-media#videocodec) and [`audioCodec`](/docs/webcodecs/convert-media#audiocodec) options to transform all tracks to the codecs you specify.

```tsx twoslash title="Choosing video and audio codecs"
import {convertMedia} from '@remotion/webcodecs';

await convertMedia({
  src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
  container: 'webm',
  videoCodec: 'vp8',
  audioCodec: 'opus',
});
```

## Handle each track individually

With the [`onVideoTrack`](/docs/webcodecs/convert-media#onvideotrack) and [`onAudioTrack`](/docs/webcodecs/convert-media#onaudiotrack) callbacks, you can decide for each track what to do with it.

```tsx twoslash title="Using the onVideoTrack() API"
import {convertMedia} from '@remotion/webcodecs';

await convertMedia({
  src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
  container: 'webm',
  audioCodec: 'opus',
  onVideoTrack: ({track}) => {
    if (track.codecEnum === 'vp8') {
      return {type: 'copy'};
    }

    return {type: 'reencode', videoCodec: 'vp8'};
  },
});
```

[`onVideoTrack`](/docs/webcodecs/convert-media#onvideotrack) and [`onAudioTrack`](/docs/webcodecs/convert-media#onaudiotrack) have a higher priority than [`videoCodec`](/docs/webcodecs/convert-media#videocodec) and [`audioCodec`](/docs/webcodecs/convert-media#audiocodec).

The options for video codecs are:

- `{"type": "copy"}` - Copy the track without re-encoding
- `{"type": "reencode", "videoCodec": ConvertMediaVideoCodec}` - Re-encode the track into the specified codec
- `{"type": "drop"}` - Remove the track from the output
- `{"type": "fail"}` - Fail and stop the conversion process

The options for audio codecs are:

- `{"type": "copy"}` - Copy the track without re-encoding
- `{"type": "reencode", "audioCodec": ConvertMediaAudioCodec; bitrate: number; sampleRate: number | null;}` - Re-encode the track into the specified codec. The suggested bitrate to use is `128000`. The sample rate can be `null` to use the sample rate of the original track.
- `{"type": "drop"}` - Remove the track from the output
- `{"type": "fail"}` - Fail and stop the conversion process

The enums `ConvertMediaVideoCodec` and `ConvertMediaAudioCodec` can be imported from `@remotion/webcodecs`.

## Checking if a track can be copied

To check if it is possible to return `{"type": "copy"}`, you can use `canCopyTrack` property you get from `onVideoTrack`.

```tsx twoslash title="Using the canCopyVideoTrack() API"
import {convertMedia} from '@remotion/webcodecs';

await convertMedia({
  src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
  container: 'webm',
  audioCodec: 'opus',
  onVideoTrack: ({track, inputContainer, outputContainer, canCopyTrack}) => {
    if (canCopyTrack) {
      return {type: 'copy'};
    }

    return {type: 'reencode', videoCodec: 'vp8'};
  },
});
```

To check outside of a `onVideoTrack` handler, you can also use the the [`canCopyVideoTrack()`](/docs/webcodecs/can-copy-video-track) and [`canCopyAudioTrack()`](/docs/webcodecs/can-copy-audio-track) APIs

## Checking if a track can be re-encoded

To check if it is possible to return `{"type": "reencode"}`, you can use the [`canReencodeVideoTrack()`](/docs/webcodecs/can-reencode-video-track) and [`canReencodeAudioTrack()`](/docs/webcodecs/can-reencode-audio-track) APIs.

```tsx twoslash title="Using the canReencodeVideoTrack() API"
import {convertMedia, canReencodeVideoTrack} from '@remotion/webcodecs';

await convertMedia({
  src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
  container: 'webm',
  audioCodec: 'opus',
  onVideoTrack: async ({track}) => {
    const canReencode = await canReencodeVideoTrack({
      videoCodec: 'vp8',
      track,
      resizeOperation: null,
      rotate: 0,
    });

    if (canReencode) {
      return {type: 'reencode', videoCodec: 'vp8'};
    }

    return {type: 'drop'};
  },
});
```

## Asynchronously determining action

The [`onAudioTrack`](/docs/webcodecs/convert-media#onaudiotrack) and [`onVideoTrack`](/docs/webcodecs/convert-media#onvideotrack) callbacks can be asynchronous.  
While the operations are unresolved, reading of the input fill is paused.

## Decide behavior upfront

If you want to display a UI letting the user choose codec settings before the conversion starts, you can do so.

Use [`parseMedia()`](/docs/media-parser/parse-media) to get video and audio tracks respectively:

```tsx twoslash title="Using parseMedia() to get tracks upfront."
import {parseMedia} from '@remotion/media-parser';

const {tracks} = await parseMedia({
  src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
  fields: {
    tracks: true,
  },
});
```

You now have an object of two arrays of `VideoTrack` and `AudioTrack` objects.

You can now use [`canReencodeAudioTrack()`](/docs/webcodecs/can-reencode-audio-track), [`canReencodeVideoTrack()`](/docs/webcodecs/can-reencode-video-track), [`canCopyAudioTrack()`](/docs/webcodecs/can-copy-audio-track), and [`canCopyVideoTrack()`](/docs/webcodecs/can-copy-video-track) to determine which options to show.

Use the [`onVideoTrack`](/docs/webcodecs/convert-media#onvideotrack) and [`onAudioTrack`](/docs/webcodecs/convert-media#onaudiotrack) callbacks to return the user selection.  
You can use the `trackId` field as the unique key for each track.

## Falling back to default

The default values for [`onVideoTrack`](/docs/webcodecs/convert-media#onvideotrack) and [`onAudioTrack`](/docs/webcodecs/convert-media#onaudiotrack) are the functions [`defaultOnVideoTrackHandler`](/docs/webcodecs/default-on-video-track-handler) and [`defaultOnAudioTrackHandler`](/docs/webcodecs/default-on-audio-track-handler) respectively.

If you only want to override part of the logic, you can return the default resolver functions at the end of your logic.

```tsx twoslash title="Falling back to the default behavior"
import {convertMedia, defaultOnAudioTrackHandler} from '@remotion/webcodecs';

await convertMedia({
  src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
  container: 'webm',
  onAudioTrack: (params) => {
    // Custom logic for handling video tracks
    // ...

    // Fall back to the default behavior
    return defaultOnAudioTrackHandler(params);
  },
});
```

## Debugging

Pass [`logLevel: "verbose"`](/docs/webcodecs/convert-media#loglevel) to [`convertMedia()`](/docs/webcodecs/convert-media) to see debug information in the console, including how the defaults have decided which operations to take.

## Reference implementation

Visit the [source code](https://github.com/remotion-dev/remotion/tree/main/packages/convert) for [convert.remotion.dev](https://convert.remotion.dev) to see a reference implementation for an online video converter that displays a user interface for all possible options.

## See also

- [`@remotion/webcodecs`](/docs/webcodecs)
- [`convertMedia()`](/docs/webcodecs/convert-media)
